export declare interface StairsOptions {
  container: string
  floors: string
  navLinks: string
  activeClassName?: string
  datasetIndex?: string
  offsetTop?: number
}

export default class Stairs {
  constructor(options?: StairsOptions) {
    if (options) this.init(options)
  }

  // 楼层导航默认配置
  private readonly defaultOptions = {
    activeClassName: 'active',
    datasetIndex: 'index'
  }

  // 楼层导航配置
  private options!: StairsOptions

  // 滚动容器
  private container!: HTMLElement

  // 楼层元素
  private floors!: NodeListOf<HTMLElement>

  // 导航元素
  private navLinks!: NodeListOf<HTMLElement>

  // 开始监听
  private observe() {
    this.container.addEventListener('scroll', this.onScroll.bind(this))

    this.navLinks.forEach(navLink => {
      navLink.addEventListener('click', this.onNavLinkClick.bind(this, navLink))
    })
  }

  // 处理容器滚动
  private onScroll() {
    this.floors.forEach(floor => {
      const [top, bottom] = [floor.offsetTop, floor.offsetTop + floor.offsetHeight]

      const scrollTop = this.container.scrollTop + this.container.offsetTop + this.options.offsetTop!

      if (top <= scrollTop && bottom >= scrollTop) {
        const datasetIndex = floor.dataset[this.options.datasetIndex!]

        const navLink = document.querySelector(
          `${this.options.navLinks}[data-${this.options.datasetIndex}="${datasetIndex}"]`
        )

        this.navLinks.forEach(navLink => navLink.classList.remove(this.options.activeClassName!))

        navLink?.classList.add(this.options.activeClassName!)
      }

      if (this.container.scrollTop + this.container.clientHeight >= this.container.scrollHeight) {
        this.navLinks.forEach(navLink => navLink.classList.remove(this.options.activeClassName!))

        this.navLinks[this.navLinks.length - 1].classList.add(this.options.activeClassName!)
      }
    })
  }

  // 处理导航项点击
  private onNavLinkClick(navLink: HTMLElement, event: MouseEvent) {
    event.preventDefault()

    const datasetIndex = navLink.dataset[this.options.datasetIndex!]

    const floor = document.querySelector(`${this.options.floors}[data-${this.options.datasetIndex}="${datasetIndex}"]`)

    floor?.scrollIntoView()

    setTimeout(() => {
      this.navLinks.forEach(navLink => navLink.classList.remove(this.options.activeClassName!))

      navLink.classList.add(this.options.activeClassName!)
    }, 50)
  }

  // 校验配置项
  private validate(options: StairsOptions) {
    if (!options) throw new Error(`[Error] arguments[0] is required but got ${options}`)

    if (!options.container) {
      // eslint-disable-next-line @typescript-eslint/no-base-to-string
      throw new Error(`[Error] arguments[0].container is required HTMLElement but got ${options.container}`)
    }

    if (!options.floors) {
      // eslint-disable-next-line @typescript-eslint/no-base-to-string
      throw new Error(`[Error] arguments[0].floors is required NodeListOf<HTMLElement> but got ${options.floors}`)
    }

    if (!options.navLinks) {
      // eslint-disable-next-line @typescript-eslint/no-base-to-string
      throw new Error(`[Error] arguments[0].navLinks is required NodeListOf<HTMLElement> but got ${options.navLinks}`)
    }
  }

  // 初始化
  public init(options: StairsOptions) {
    this.disconnect()

    this.validate(options)

    this.options = {
      ...options,
      activeClassName: options.activeClassName || this.defaultOptions.activeClassName,
      datasetIndex: options.datasetIndex || this.defaultOptions.datasetIndex,
      offsetTop: options.offsetTop || 0
    }

    this.container = document.querySelector(this.options.container)!

    this.floors = document.querySelectorAll(this.options.floors)

    this.navLinks = document.querySelectorAll(this.options.navLinks)

    if (!this.container || !this.floors.length || !this.navLinks.length) return

    this.container.scrollTop = 0

    this.navLinks.forEach((navLink, index) => {
      navLink.classList.remove(this.options.activeClassName!)
      navLink.dataset[this.options.datasetIndex!] = navLink.dataset[this.options.datasetIndex!] || `${index}`
    })

    this.navLinks[0].classList.add(this.options.activeClassName!)

    this.floors.forEach((floor, index) => {
      floor.dataset[this.options.datasetIndex!] = floor.dataset[this.options.datasetIndex!] || `${index}`
    })

    this.observe()
  }

  // 设置当前激活的楼层
  public setActive(datasetIndex: StairsOptions['datasetIndex']) {
    const navLink = Array.from(this.navLinks).find(floor => {
      return floor.dataset[this.options.datasetIndex!] === datasetIndex
    })

    navLink?.click()
  }

  // 停止监听
  public disconnect() {
    this.container?.removeEventListener('scroll', this.onScroll)

    this.navLinks?.forEach(navLink => navLink.removeEventListener('click', this.onNavLinkClick.bind(this, navLink)))
  }
}
